
/**
 * @name MasslessMoistureFlux (sequential moisture flux calculation)
 * Calculates surface moisture flux
 * for January 2-31, 1980 
 * Part of Pacific Northwest Climate Data Analysis
 *
 * @version 0.5.003 beta, sequential moisture flux calculation, 3rd commit
 * @date 10/09/2013
 * @author Niko Simonson
 * @author Brett Yasutake
 *
 */

// IMPORTS
import java.io.IOException;     // IO Exceptions
import java.util.*;             // Vectors, etc.
import java.util.AbstractMap.*; // for simple key-value pairs
import ucar.ma2.ArrayFloat;     // for netCDF reader
import ucar.ma2.IndexIterator;  // for netCDF reader
import ucar.nc2.NetcdfFile;     // for netCDF reader
import ucar.nc2.Variable;       // for netCDF reader

public class MasslessMoistureFlux {

    // Moisture flux constants
    private static final int DAYS = 30;          // days of data collection
    private static final int DAYSEGMENTS = 4;    // time slices per day
    
    // Target variable
    private static final String XWIND = "U10"; // east-west windspeed at 10m above surface
    private static final String YWIND = "V10"; // north-south windspeed at 10m above surface
    private static final String MOISTURE = "Q2"; // moisture at 2m above surface
    
    // Northwest regional climate data sample
    private static final String FILENAMEBEGIN = "/home/brett/netcdf_files/1980_jan/wrfout_d02_1980-01-";
    private static final String FILENAMEEND = ":00:00";  // end of file name
    private static final int XRANGE = 123;
    private static final int YRANGE = 162;
    
    // VARIABLES
    private static int myX; // time slices in a day
    private static int myY; // days
    private static double[][] mf; // moisture flux scalar
    private static double[][] dir; // moisture flux direction in radians
    private static ResultsAdapter[][] ra;


    /**
     * @name MasslessMoistureFlux
     * @param interval 
     */
    public MasslessMoistureFlux(Object interval) {

    }

    /**
     * @name init
     *
     * @param args
     * @return unused
     */
    public static void init(Object args) {

        mf = new double[XRANGE][YRANGE];
        dir = new double[XRANGE][YRANGE];
        ra = new ResultsAdapter[DAYSEGMENTS][DAYS];
        String[][] resultsName = new String[DAYSEGMENTS][DAYS];
        Integer[] dimensions = new Integer[2];
        dimensions[0] = XRANGE;
        dimensions[1] = YRANGE;
       
        SimpleEntry<String, Integer[]> entryMagnitude;
        entryMagnitude = new SimpleEntry<String, Integer[]>("magnitude", dimensions);
        SimpleEntry<String, Integer[]> entryDirection = new SimpleEntry<String, Integer[]>("direction", dimensions);
        for (int x = 0; x < DAYSEGMENTS; ++x) {
            for (int y = 0; y < DAYS; ++y) {
                resultsName[x][y] = "MoistureFlux" + y + "-" + x;
                ra[x][y] = new ResultsAdapter(ResultsAdapter.DOUBLE, resultsName[x][y], entryMagnitude, entryDirection);
            }
        }
        myX = 0; 
        myY = 0;
    }

    /**
     * @name computeMF
     * Open netCDF file and compute moisture flux over
     * the entire region
     *
     * @param args unused
     * @return true = success, false = exception occurred
     */
    public static Object computeMF(Object args) {
        // VARIABLES
        NetcdfFile stubby = null; // target netCDF file
        String fileName;
        Variable xSpd, ySpd, qv;
        ArrayFloat.D3 d3x, d3y, d3q;
        double xW, yW, q;

        try {
            // open target netCDF file
            fileName = getFileName(myX, myY);
            stubby = NetcdfFile.open(fileName, null);	// Open the file

            // Find target variable
            xSpd = stubby.findVariable(XWIND);
            ySpd = stubby.findVariable(YWIND);
            qv = stubby.findVariable(MOISTURE);
            
            // Get list of file's variables
            List<Variable> stubbyVars = stubby.getVariables();
            xSpd = stubbyVars.get(stubbyVars.indexOf(xSpd));
            ySpd = stubbyVars.get(stubbyVars.indexOf(ySpd));
            qv = stubbyVars.get(stubbyVars.indexOf(qv));

            // Read values from variable (temperatures over the region)
            d3x = (ArrayFloat.D3) xSpd.read();
            d3y = (ArrayFloat.D3) ySpd.read();
            d3q = (ArrayFloat.D3) qv.read();

            // iterators
            IndexIterator iterX, iterY, iterQ;

            int x = 0;
            int y = 0;

            iterX = d3x.getIndexIterator();
            iterY = d3y.getIndexIterator();
            iterQ = d3q.getIndexIterator();
            
            // Fill array with results
            while (iterX.hasNext()) {
                iterX.next();
                iterY.next();
                iterQ.next();
                
                xW = (double)iterX.getFloatCurrent();
                yW = (double)iterY.getFloatCurrent();
                q = (double)iterQ.getFloatCurrent();
                
                mf[x][y] = q * Math.sqrt(Math.pow(xW, 2.0) + Math.pow(yW, 2.0));
                if (yW != 0) {
                    dir[x][y] = Math.atan2(xW, yW);
                }
                else if (xW > 0) {
                    dir[x][y] = 0.0;
                }
                else if (xW < 0) {
                    dir[x][y] = Math.PI;
                }
                else {
                    // no wind, so no moisture flux
                    dir[x][y] = 0.0;
                    mf[x][y] = 0.0;
                }
                // iterate through 2 dimensions
                if (y == YRANGE - 1) {
                    x++;
                    y = 0;
                } else {
                    y++;
                }
            }
            return (Object) true;
        } catch (java.io.IOException e) {
            e.printStackTrace();
            return (Object) false;
        } catch (Exception e) {
            e.printStackTrace();
            return (Object) false;
        } finally {		// Close out the file no matter what occurs
            if (stubby != null) {
                try {
                    stubby.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
            return (Object) null;
        }
    }

    /**
     * @name getFileName Private helper function to dynamically generate correct
     * file name
     *
     * @param thisX daily time slice
     * @param thisY day
     *
     * @return
     */
    private static String getFileName(int thisX, int thisY) {
        // VARIABLES
        String strX, strY;  // file component numbers

        // calculate correct day of the month
        strY = Integer.toString(thisY + 2);

        // calculate correct time of the day
        strX = Integer.toString(thisX * 6);

        // adjust for numbers less than 10
        if (Integer.parseInt(strX) < 10) {
            strX = "0" + strX;
        }
        if (Integer.parseInt(strY) < 10) {
            strY = "0" + strY;
        }
        // return proper file name
        return FILENAMEBEGIN + strY + "_" + strX + FILENAMEEND;
    }
    
     /**
     * @name storeMFResults Record results to the results adapter
     * @param args
     * @return unused
     */
    public static Object storeMFResults(Object args) {

        ra[myX][myY].log2DVar("magnitude", mf);
        ra[myX][myY].log2DVar("direction", dir);
        
        return null;
    }

    /**
     * @name runAnalysis Performs entire analysis
     *
     * @param stat StatusModel UI view
     * @param initialized set to true if environment has already been initialized
     * @throws Exception
     */
    public static void runAnalysis(StatusModel stat) throws Exception {
        // VARIABLES
        long start, end, totalTime;     // data provenance for execution time
        int count = DAYS * DAYSEGMENTS; // total number of files

        StatusModel sm = stat;
        
        try {
            // begin time measurement
            start = new Date().getTime();
            
            sm.reportMessage("initialization");
            init(null);

            sm.reportMessage("Starting analysis");
            for (int i = 0; i < count; ++i) {
                sm.reportMessage("opening file: " + myX + ", " + myY);
                computeMF(null);
                sm.reportMessage("storing results");
                storeMFResults(null);
                
                ++myX;
                if (myX == DAYSEGMENTS) {
                    myX = 0;
                    ++myY;
                }
            }     
            end = new Date().getTime();
            totalTime = end - start;    //milliseconds
            
            System.err.println("Total execution time (milliseconds): "
                    + totalTime);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
} // END sequential moisure flux imlpementation
